{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Convert static computational graph to ONNX"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Prepare environment"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install paddlepaddle paddle2onnx # required, paddlepaddle>=1.8.0, paddle2onnx>=0.3.2\n",
    "!pip install onnx onnxruntime # optional for check and run ONNX model, onnx>=1.7.0, onnxruntime>=1.5.2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import paddle.fluid as fluid\n",
    "import os"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. ResNet network"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def conv_bn_layer(input,\n",
    "                  ch_out,\n",
    "                  filter_size,\n",
    "                  stride,\n",
    "                  padding,\n",
    "                  act='relu',\n",
    "                  bias_attr=False):\n",
    "    tmp = fluid.layers.conv2d(\n",
    "        input=input,\n",
    "        filter_size=filter_size,\n",
    "        num_filters=ch_out,\n",
    "        stride=stride,\n",
    "        padding=padding,\n",
    "        act=None,\n",
    "        bias_attr=bias_attr)\n",
    "    return fluid.layers.batch_norm(input=tmp, act=act)\n",
    "\n",
    "def shortcut(input, ch_in, ch_out, stride):\n",
    "    if ch_in != ch_out:\n",
    "        return conv_bn_layer(input, ch_out, 1, stride, 0, None)\n",
    "    else:\n",
    "        return input\n",
    "\n",
    "def basicblock(input, ch_in, ch_out, stride):\n",
    "    tmp = conv_bn_layer(input, ch_out, 3, stride, 1)\n",
    "    tmp = conv_bn_layer(tmp, ch_out, 3, 1, 1, act=None, bias_attr=True)\n",
    "    short = shortcut(input, ch_in, ch_out, stride)\n",
    "    return fluid.layers.elementwise_add(x=tmp, y=short, act='relu')\n",
    "\n",
    "def layer_warp(block_func, input, ch_in, ch_out, count, stride):\n",
    "    tmp = block_func(input, ch_in, ch_out, stride)\n",
    "    for i in range(1, count):\n",
    "        tmp = block_func(tmp, ch_out, ch_out, 1)\n",
    "    return tmp\n",
    "\n",
    "def resnet_cifar10(ipt, depth=32):\n",
    "    # depth should be one of 20, 32, 44, 56, 110, 1202\n",
    "    assert (depth - 2) % 6 == 0\n",
    "    n = (depth - 2) // 6\n",
    "    nStages = {16, 64, 128}\n",
    "    conv1 = conv_bn_layer(ipt, ch_out=16, filter_size=3, stride=1, padding=1)\n",
    "    res1 = layer_warp(basicblock, conv1, 16, 16, n, 1)\n",
    "    #res2 = layer_warp(basicblock, res1, 16, 32, n, 2)\n",
    "    #res3 = layer_warp(basicblock, res2, 32, 64, n, 2)\n",
    "    pool = fluid.layers.pool2d(\n",
    "        input=res1, pool_size=8, pool_type='avg', pool_stride=1)\n",
    "    predict = fluid.layers.fc(input=pool, size=10, act='softmax')\n",
    "    return predict\n",
    "\n",
    "data_shape = [None, 3, 32, 32]\n",
    "images = fluid.data(name='pixel', shape=data_shape, dtype='float32')\n",
    "predict = resnet_cifar10(images, 32)\n",
    "exe = fluid.Executor(fluid.CPUPlace())\n",
    "_ =exe.run(fluid.default_startup_program())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Convert uncombined PaddlePaddle model(parameters saved in separated files)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['save_infer_model/scale_0.tmp_0']"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model_dir = './resnet_not_combined/'\n",
    "fluid.io.save_inference_model(model_dir, [\"pixel\"], [predict], exe)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "__model__         batch_norm_2.w_2  batch_norm_6.w_2  conv2d_10.w_0\n",
      "batch_norm_0.b_0  batch_norm_3.b_0  batch_norm_7.b_0  conv2d_2.b_0\n",
      "batch_norm_0.w_0  batch_norm_3.w_0  batch_norm_7.w_0  conv2d_2.w_0\n",
      "batch_norm_0.w_1  batch_norm_3.w_1  batch_norm_7.w_1  conv2d_3.w_0\n",
      "batch_norm_0.w_2  batch_norm_3.w_2  batch_norm_7.w_2  conv2d_4.b_0\n",
      "batch_norm_1.b_0  batch_norm_4.b_0  batch_norm_8.b_0  conv2d_4.w_0\n",
      "batch_norm_1.w_0  batch_norm_4.w_0  batch_norm_8.w_0  conv2d_5.w_0\n",
      "batch_norm_1.w_1  batch_norm_4.w_1  batch_norm_8.w_1  conv2d_6.b_0\n",
      "batch_norm_1.w_2  batch_norm_4.w_2  batch_norm_8.w_2  conv2d_6.w_0\n",
      "batch_norm_10.b_0 batch_norm_5.b_0  batch_norm_9.b_0  conv2d_7.w_0\n",
      "batch_norm_10.w_0 batch_norm_5.w_0  batch_norm_9.w_0  conv2d_8.b_0\n",
      "batch_norm_10.w_1 batch_norm_5.w_1  batch_norm_9.w_1  conv2d_8.w_0\n",
      "batch_norm_10.w_2 batch_norm_5.w_2  batch_norm_9.w_2  conv2d_9.w_0\n",
      "batch_norm_2.b_0  batch_norm_6.b_0  conv2d_0.w_0      fc_0.b_0\n",
      "batch_norm_2.w_0  batch_norm_6.w_0  conv2d_1.w_0      fc_0.w_0\n",
      "batch_norm_2.w_1  batch_norm_6.w_1  conv2d_10.b_0\n"
     ]
    }
   ],
   "source": [
    "!ls ./resnet_not_combined/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Execute the shell command. Jupyter may can't find the paddle2onnx command, \n",
    "# please go to the command line to execute\n",
    "\n",
    "!paddle2onnx --model_dir ./resnet_not_combined/  --save_file onnx-model/model.onnx --opset_version 10"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Convert combined PaddlePaddle model(parameters saved in one binary file)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['save_infer_model/scale_0.tmp_1']"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model_dir = './resnet_combined/'\n",
    "fluid.io.save_inference_model(model_dir, [\"pixel\"], [predict], exe, model_filename='__model__', params_filename='__params__')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "__model__  __params__\n"
     ]
    }
   ],
   "source": [
    "!ls ./resnet_combined/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Execute the shell command. Jupyter may can't find the paddle2onnx command, \n",
    "# please go to the command line to execute\n",
    "\n",
    "!paddle2onnx --model_dir ./resnet_combined/ --model_filename '__model__' --params_filename '__params__' --save_file onnx-model/model.onnx --opset_version 10"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
